package com.ym.core.interceptor;

import cn.hutool.crypto.SecureUtil;
import com.ym.common.constant.Coder;
import com.ym.common.utils.ServletUtil;
import com.ym.core.annotation.RepeatSubmit;
import com.ym.core.domain.APIResponse;
import com.ym.core.redis.RedisKeyEnum;
import com.ym.core.redis.RedisUtil;
import com.ym.core.shiro.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 防止重复提交拦截器
 *
 * @author bird
 */
@Component
@Slf4j
public class RepeatSubmitInterceptor extends HandlerInterceptorAdapter {

	@Autowired
	private RedisUtil redisUtil;

	/**
	 * 间隔时间，单位:秒 默认10秒
	 * <p>
	 * 两次相同参数的请求，如果间隔时间大于该参数，系统不会认定为重复提交的数据
	 */
	private int intervalTime = 3;


	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		if (handler instanceof HandlerMethod) {
			HandlerMethod handlerMethod = (HandlerMethod) handler;
			Method method = handlerMethod.getMethod();
			RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
			if (annotation != null) {
				if (this.isRepeatSubmit(request)) {
					ServletUtil.write(response, APIResponse.fail(Coder.System.REPEATED_SUBMIT), MediaType.APPLICATION_JSON_UTF8_VALUE);
					return false;
				}
			}
			return true;
		} else {
			return super.preHandle(request, response, handler);
		}
	}

	/**
	 * 验证是否重复提交由子类实现具体的防重复提交的规则
	 *
	 * @param request
	 * @return
	 * @throws Exception
	 */
	public boolean isRepeatSubmit(HttpServletRequest request) {
		Long loginId = ShiroUtil.getLoginId(true);
		if (loginId == null) {
			return false;
		}
		// 本次参数及系统时间
		String url = request.getRequestURI();
		String urlParam = String.valueOf(ServletUtil.getParamMap(request));
		String bodyParam = ServletUtil.getBody(request);
		String headerParam = String.valueOf(ServletUtil.getHeaderMap(request));
		String md5Value = SecureUtil.md5(url + urlParam + bodyParam + headerParam + loginId);
		String repeatKey = RedisKeyEnum.REPEAT_KEY.build(md5Value);
		boolean hasKey = redisUtil.hasKey(repeatKey);
		if (!hasKey) {
			// 设定失效时间
			redisUtil.set(repeatKey, loginId.toString(), intervalTime, TimeUnit.SECONDS);
		} else {
			log.error("重复提交表单:{}-{}-{}-{}-{}", loginId, url, urlParam, bodyParam, headerParam);
		}
		return hasKey;
	}

}
